Spring容器
功能
- 通过 IoC 和 DI 管理对象的创建和依赖关系,提供强大的生命周期管理、事务管理、AOP 支持、事件处理、资源配置
控制反转(IoC)
功能
- 将创建和管理对象的责任从应用程序代码转移到一个框架或者容器中(反转),从而不再需要关心对象是如何被创建和管理的(低耦合),而是可以专注于业务逻辑,加强各个功能模块内部的功能联系(高内聚)
依赖注入(DI)
功能
- 将对象所需的依赖关系从外部注入到对象中,而不是让对象自己创建或查找这些依赖,从而降低对象之间的耦合度,实现控制反转
使用
@Componect
:用于将一个Java类标记为可被Spring容器管理的组件。用于实现自动化依赖管理@Autowired
:通过在类的字段、构造函数或方法上添加@Autowired注解,让Spring容器自动解析并注入相应的依赖对象。用于实现自动化的依赖注入
// UserRepository 类的实现,使用 @Repository 标记数据访问组件,是 @Componect 的一个特化
@Repository
public class UserRepository {
public void saveUser(String user) {
System.out.println("Saving user: " + user);
}
}
// UserService 类的实现, 使用 @Service 标记服务层组件,是 @Componect 的一个特化
@Service
public class UserService {
private final UserRepository userRepository;
// 在构造函数上自动注入一个UserRepository的实例,不需要自己手动创建了
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 在字段上
@Autowired
private UserRepository userRepository;
// 在setter方法上
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser(User user) {
userRepository.saveUser(user);
}
}
==@Autowired注解默认按照类型进行注入,如果存在多个相同类型的bean,就会报错,解决方案如下==
@Resource
:使用@Resource(“classImpl”)来指定注入哪个一个类
@Resource("myServiceImpl1")
private MyService myService;
@Qualifier
:也可以在@Autowired
注解下添加该注解,指定让 value 的 bean 生效
@Autowired
public PaymentProcessor(@Qualifier("creditCardPaymentService") PaymentService paymentService) {
this.paymentService = paymentService;
}
@Primary
:提升注入优先级,使当前bean生效
// 两个实现相同接口的类,使用 @Autowired 默认注入的话,spring 容器会不知道用哪个
public interface PaymentService {
void processPayment();
}
@Service
@Primary // 届时使用 @Autowired 时,优先使用该bean对象
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing payment via credit card");
}
}
@Service
public class PaypalPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing payment via PayPal");
}
}
Bean对象
功能
- 由Spring容器创建、组装和管理的对象实例,用于依赖注入
特点
- 元数据定义: Spring Beans 通常是通过 XML 文件、注解 或 Java 配置类 等元数据被定义
- 生命周期管理:Spring 容器负责管理 Bean 的整个生命周期,包括创建、依赖注入、初始化、使用和销毁
- 单例或多例:默认情况下,Spring Beans 是单例的,但也可以配置为每次请求时创建一个新的实例
- 自动装配:Spring 可以自动识别并装配 Bean 的依赖关系
使用
@Componect
:声明Bean对象的基础注解,不属于以下三类时,用此注解@Controller
:@Componect的衍生注解,标注在控制器上@Service
:@Componect的衍生注解,标注在业务类上@Respository
:@Componect的衍生注解,标注在数据访问类上(由于与mybatis整合,使用较少)
细节
- 声明 bean 时,可以通过value指定bean的名称,如果没有指定,默认为类名首字母小写
// 使用value属性指定bean的名称
@Componect(name = "customService")
public MyService myService() {
return new MyService();
}
// 没有指定value属性,将使用 *类名首字母小写* 作为bean的名称 ,即anotherService
@Componect
public AnotherService newService() {
return new AnotherService();
}
- bean注解要生效,需要被组件扫描注解
@ComponentScan
扫描,默认扫描的范围是启动类所在包及其子包(在启动类Application.java的@SpringBootApplication
中已经包含这个注解)。也可以指定需要扫描的包。
应用上下文(Application Context)
功能
- 作为 spring容器 的一个接口,能够创建spring容器,负责创建、配置和管理 Bean (生命周期管理)
- 作为 BeanFactory 的子接口,提供了更多的高级特性,如国际化支持、事件传播、Web 应用上下文等
使用
- 当Spring Boot应用启动时,首先会创建一个ApplicationContext实例。
- 如果类路径上存在spring-boot-starter-web依赖,那么可能会创建一个AnnotationConfigServletWebServerApplicationContext实例,这是一个用于Web应用的上下文。
- 加载注册所有的单例bean,并通过容器进行管理
分层架构
表示层
功能
- 负责与用户交互,展示数据(前端技术)和接收用户的输入(Controller)
- 后端接收前端发送的请求,对请求进行分发处理,并响应数据
细节
- 放在
src/.../contoller
软件包下
服务层
功能
- 处理来自表示层的请求,执行业务规则,并与数据访问层交互
细节
- 放在
src/.../service
软件包下
数据访问层
功能
- 负责与数据库或其他数据源交互,执行数据的持久化操作
细节
- 该层通常包含 数据访问对象(Repositories)和 实体(Entities)
- 分别放在
src/.../repository
和src/.../entities
软件包下
基础设施层
功能
- 提供支持应用程序运行的基础服务,如数据库连接、消息队列、邮件服务、缓存等
MVC架构
功能
- 将应用程序的逻辑层(模型)和表现层(视图)分离,提高了代码的可维护性和可扩展性。当需要修改界面或业务逻辑时,可以独立修改模型或视图,而不需要修改另一部分代码。
实现
- 模型(Model):模型封装了应用程序的数据和业务逻辑。在 Spring MVC 中,模型通常是一个简单的 POJO(Plain Old Java Object)。
- 视图(View):视图负责展示模型数据。在 Spring MVC 中,视图可以是 JSP、HTML、XML 或其他格式。
- 控制器(Controller):控制器处理用户的请求,并调用模型来处理业务逻辑。然后,它将模型数据传递给视图进行展示。
面向切面编程(AOP)
功能
- Spring 通过 代理 实现 AOP,将关注点(例如日志、事务管理、安全控制等)从业务逻辑中分离出来,并通过 切面 进行模块化,从而增强代码的可维护性和可扩展性(在 OOP 中关注点可能会分散在代码的多个地方,这使得代码难以维护和扩展)
构成
- 切面(
@Aspect
):封装了程序中的一个或多个关注点,比如日志记录、事务管理等- 切点(
@Pointcut
):定义了在哪些方法上执行通知 - 通知(
@Advice
):切面中定义的一个具体的行为,可以在特定的时间点执行- 前置通知(
@Before
):目标方法执行前执行 - 后置通知(
@After
):目标方法执行后执行(无论方法是否抛出异常) - 返回通知(
@AfterReturning
):目标方法正常执行完后执行 - 异常通知(
@AfterThrowing
):目标方法抛出异常时执行 - 环绕通知(
@Around
):环绕目标方法执行,能够控制目标方法是否执行,以及修改目标方法的返回值,需要调用ProceedingJoinPoint.proceed() 来让原始方法执行,且返回类型必须指定为 Object
- 前置通知(
- 切点(
- 连接点(Joinpoint):在程序执行过程中可以插入增强的点
- 方法执行
- 对象创建和销毁
- 字段访问
- 异常处理
- 类型加载
- 代理(Proxy):Spring AOP 会在运行时创建一个代理对象,这个对象包含了目标对象的引用,并在调用目标对象的方法前后执行额外的代码(即切面)
- JDK动态代理(接口代理):要求目标对象实现一个或多个接口,代理对象会实现相同的接口,并在接口方法被调用时执行增强逻辑
- CGLIB代理(类代理):如果目标对象没有实现接口,Spring AOP可以使用CGLIB库来创建代理对象,它可以在运行时扩展Java类和实现Java接口
实现
- 引入依赖:spring-boot-starter-aop
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建
切面类
并定义通知
和切点
:- 切面类:通过
@Aspect
注解标记切面类 - 通知:在通知上传入切点,构成一个切面
- 切点表达式:使用
execution
关键字匹配Java程序中满足特定条件的方法执行点
- 切面类:通过
execution(modifiers-pattern? // 方法参数类型
ret-type-pattern // 方法的返回类型
declaring-type-pattern? // 方法所属的类
method-name-pattern // 方法名
(param-pattern) // 方法参数类型
throws-pattern?) // 方法抛出的异常类型
@Slf4j
@Component
@Aspect // 切面类声明
public class MyAspect {
//前置通知
@Before("execution(* com.itheima.service.*.*(..))")
// *:匹配任意返回类型的任意方法
// com.itheima.service.*:匹配com.itheima.service包中的任意类
// .*:匹配这个包中所有类中的任意方法
// (..):匹配任意数量和类型的参数
public void before(JoinPoint joinPoint){
log.info("before ...");
}
//环绕通知
@Around("execution(* com.itheima.service.*.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("around before ...");
//调用目标对象的原始方法执行
Object result = proceedingJoinPoint.proceed();
//原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
log.info("around after ...");
return result;
}
//后置通知
@After("execution(* com.itheima.service.*.*(..))")
public void after(JoinPoint joinPoint){
log.info("after ...");
}
//返回后通知(程序在正常执行的情况下,会执行的后置通知)
@AfterReturning("execution(* com.itheima.service.*.*(..))")
public void afterReturning(JoinPoint joinPoint){
log.info("afterReturning ...");
}
//异常通知(程序在出现异常的情况下,执行的后置通知)
@AfterThrowing("execution(* com.itheima.service.*.*(..))")
public void afterThrowing(JoinPoint joinPoint){
log.info("afterThrowing ...");
}
}
细节
- 多通知顺序:
- 切面类顺序:根据切面类名称顺序,或者使用
@order
注解控制通知顺序 - 通知顺序:同一个切面类中可能定义了多个通知,通知的执行顺序是:
@Around(start)
->@Before
->@AfterReturning
/@AfterThrowing
->@After
->@Around(end)
,相同类型通知按照类名排序顺序执行
- 切面类顺序:根据切面类名称顺序,或者使用
- 切入点表达式抽取到一个
@Pointcut
方法上,其他需要使用直接引用即可
@Pointcut("execution(* com.itheima.service.*.*(..))")
private void pt(){} //如果pt方法是public的,则在其他类中也可以使用
//前置通知
@Before("pt()")
public void before(JoinPoint joinPoint){
log.info("before ...");
}
Last updated on